#include "state.h"
#include "config.h"
#include "draw.h"
#include "input.h"
#include "utils.h"
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/extensions/Xinerama.h>
#include <cstdio>
#include <clocale>
#include <unistd.h>
#include <algorithm>

// Initialize window geometry (position, size) based on config/monitor
static void setupWindowGeometry()
{
    State &state = getState();
    if (!state.dpy || !state.root)
        return;

    int x = 0, y = 0;
    XWindowAttributes rootAttrs;
    XGetWindowAttributes(state.dpy, state.root, &rootAttrs);

    // Default to root window dimensions
    state.mw = rootAttrs.width;
    state.mh = (state.cfg.lines + 1) * state.bh; // +1 for input field

    // Use Xinerama to get monitor info (multi-monitor support)
    XineramaScreenInfo *info = XineramaQueryScreens(state.dpy, &state.screen);
    if (info && state.screen > 0)
    {
        int targetMonitor = state.cfg.monitor;
        // Fallback to focused monitor if none specified
        if (targetMonitor < 0 || targetMonitor >= state.screen)
        {
            Window focusWin;
            int revert;
            XGetInputFocus(state.dpy, &focusWin, &revert);
            for (int i = 0; i < state.screen; ++i)
            {
                XWindowAttributes winAttrs;
                if (XGetWindowAttributes(state.dpy, focusWin, &winAttrs))
                {
                    if (winAttrs.x >= info[i].x_org && winAttrs.x < info[i].x_org + info[i].width &&
                        winAttrs.y >= info[i].y_org && winAttrs.y < info[i].y_org + info[i].height)
                    {
                        targetMonitor = i;
                        break;
                    }
                }
            }
        }
        if (targetMonitor >= 0 && targetMonitor < state.screen)
        {
            // Center window if configured
            if (state.cfg.centered)
            {
                state.mw = std::min(static_cast<unsigned int>(info[targetMonitor].width), static_cast<unsigned int>(state.cfg.min_width));
                x = info[targetMonitor].x_org + (info[targetMonitor].width - state.mw) / 2;
                y = info[targetMonitor].y_org + (info[targetMonitor].height - state.mh) / 2;
            }
            else
            {
                // Align to top/bottom of monitor
                x = info[targetMonitor].x_org;
                y = state.cfg.topbar ? info[targetMonitor].y_org : info[targetMonitor].y_org + info[targetMonitor].height - state.mh;
                state.mw = info[targetMonitor].width;
            }
        }
        XFree(info);
    }
    else
    {
        // Single monitor fallback
        if (state.cfg.centered)
        {
            state.mw = std::min(static_cast<unsigned int>(rootAttrs.width), static_cast<unsigned int>(state.cfg.min_width));
            x = (rootAttrs.width - state.mw) / 2;
            y = (rootAttrs.height - state.mh) / 2;
        }
        else
        {
            x = 0;
            y = state.cfg.topbar ? 0 : rootAttrs.height - state.mh;
            state.mw = rootAttrs.width;
        }
    }

    // Create window with calculated geometry
    XSetWindowAttributes swa;
    swa.override_redirect = True; // Bypass window manager
    swa.background_pixel = state.drw->scheme[(size_t)Scheme::NORM][1].pixel;
    swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask | ButtonPressMask | FocusChangeMask;
    state.win = XCreateWindow(
        state.dpy, state.root, x, y, state.mw, state.mh, 0,
        DefaultDepth(state.dpy, DefaultScreen(state.dpy)), InputOutput,
        DefaultVisual(state.dpy, DefaultScreen(state.dpy)),
        CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);

    // Set window class for WM recognition
    XClassHint classHint = {(char *)"dmenu", (char *)"dmenu"};
    XSetClassHint(state.dpy, state.win, &classHint);

    // Embed in parent window if configured
    if (state.cfg.embed_window)
    {
        state.parentwin = static_cast<Window>(state.cfg.embed_window);
        XReparentWindow(state.dpy, state.win, state.parentwin, x, y);
    }

    // Map window to make it visible
    XMapRaised(state.dpy, state.win);
}

int main(int argc, char *argv[])
{
    // Initialize locale for Unicode support
    if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
    {
        fputs("Warning: No locale support (UTF-8 may not work)\n", stderr);
    }

    // Load configuration (file -> command-line args)
    Config cfg;
    cfg.loadFromFile(); // Load from ~/.config/dmenu/config
    if (!cfg.loadFromArgs(argc, argv))
    {
        usage(); // Print help and exit on invalid args
    }

    // Open X11 display
    Display *dpy = XOpenDisplay(nullptr);
    if (!dpy)
    {
        fprintf(stderr, "Error: Could not open display\n");
        return EXIT_FAILURE;
    }

    // Initialize global state
    State &state = getState();
    state.cfg = cfg;
    state.dpy = dpy;
    state.screen = DefaultScreen(dpy);
    state.root = RootWindow(dpy, state.screen);

    // Create drawing context and load resources
    Drw drw(dpy, state.screen, state.root, 1, 1); // Temp size (resized later)
    if (!drw.loadFonts(cfg.fonts))
    {
        fprintf(stderr, "Error: Could not load any fonts\n");
        XCloseDisplay(dpy);
        return EXIT_FAILURE;
    }
    drw.allocColors(cfg);
    state.drw = &drw;

    // Initialize UI metrics
    state.bh = drw.fonts[0]->h + 4;    // Item height (font + padding)
    state.lrpad = drw.fonts[0]->h / 2; // Left/right text padding
    state.promptw = !cfg.prompt.empty() ? drw.getTextWidth(cfg.prompt.c_str()) + state.lrpad : 0;
    state.inputw = std::max(static_cast<unsigned int>(cfg.min_width) / 3, drw.getTextWidth("placeholder") + state.lrpad);

    // Initialize atoms for clipboard/UTF-8
    state.clip = XInternAtom(dpy, "CLIPBOARD", False);
    state.utf8 = XInternAtom(dpy, "UTF8_STRING", False);

    // Read items from stdin and initialize input method
    readStdin();
    state.xic = XCreateIC(
        XOpenIM(dpy, nullptr, nullptr, nullptr),
        XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
        XNClientWindow, state.win,
        XNFocusWindow, state.win,
        nullptr);

    // Setup window and grab input
    setupWindowGeometry();
    drw.resize(state.mw, state.mh); // Resize drawable to window size
    grabKeyboard();
    grabFocus();

    // Draw initial menu and start event loop
    drw.drawMenu();
    runEventLoop();

    // Cleanup (should never reach here normally)
    cleanup();
    return EXIT_SUCCESS;
}
